package cz.data.domain.market.mapping.handler;

import cz.data.common.security.InMemoryRequestMatcherRepository;
import cz.data.common.utils.SpringContextHolder;
import cz.data.domain.market.mapping.listener.event.ApiEventType;
import cz.data.domain.market.mapping.listener.event.TableApiReleaseEvent;
import cz.data.domain.market.model.ApiResourceVo;
import cz.data.domain.market.model.MarketUtil;
import cz.data.domain.market.model.dto.TableApiSettings;
import cz.data.domain.market.model.entity.TableApiEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class TableApiHandlerMapping implements InitializingBean {

    private static final Map<String, TableApiEntity> mappings = new ConcurrentHashMap<>();

    @Resource
    RequestMappingHandlerMapping requestMappingHandlerMapping;
    @Resource
    TableApiRequestHandler handler;
    @Resource
    InMemoryRequestMatcherRepository matcherRepository;

    private Method handlerMethod;

    @Override
    public void afterPropertiesSet() throws Exception {
        this.handlerMethod = TableApiRequestHandler.class.getDeclaredMethod("invoke",
                HttpServletRequest.class, HttpServletResponse.class,
                Map.class, Map.class, Map.class, Map.class);
    }

    public static TableApiEntity getMappingApiInfo(HttpServletRequest request) {
        NativeWebRequest webRequest = new ServletWebRequest(request);
        String requestMapping = (String) webRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
        return getMappingApiInfo(buildMappingKey(request.getMethod(), requestMapping));
    }

    public static TableApiEntity getMappingApiInfo(String key) {
        return mappings.get(key);
    }

    public static String buildMappingKey(String requestMethod, String requestMapping) {
        return requestMethod.toUpperCase() + ":" + requestMapping;
    }

    public void registerMapping(TableApiEntity api) {
        TableApiSettings settings = api.getSettings();
        List<String> operates = settings.getOperates();
        for (String operate : operates) {
            ApiResourceVo resource = MarketUtil.buildApiResource(operate, api.getApiUrl());
            String method = resource.getMethod();
            String url = resource.getUrl();
            String mappingKey = buildMappingKey(method, url);
            if (mappings.containsKey(mappingKey)) {
                // 取消注册
                mappings.remove(mappingKey);
                matcherRepository.unRegister(method, url);
                requestMappingHandlerMapping.unregisterMapping(getRequestMapping(method, url));
            }
            log.info("注册接口:{} - {} {}", api.getApiName(), method, url);
            RequestMappingInfo requestMapping = getRequestMapping(method, url);
            mappings.put(mappingKey, api);
            matcherRepository.register(method, url);
            requestMappingHandlerMapping.registerMapping(requestMapping, this.handler, this.handlerMethod);
            SpringContextHolder.publishEvent(new TableApiReleaseEvent(api, ApiEventType.Release));
        }
    }

    public void unregisterMapping(TableApiEntity api) {
        TableApiSettings settings = api.getSettings();
        List<String> operates = settings.getOperates();
        for (String operate : operates) {
            ApiResourceVo resource = MarketUtil.buildApiResource(operate, api.getApiUrl());
            String method = resource.getMethod();
            String url = resource.getUrl();
            String mappingKey = buildMappingKey(method, url);
            if (mappings.containsKey(mappingKey)) {
                // 取消注册
                log.info("注销接口:{} - {} {}", api.getApiName(), method, url);
                mappings.remove(mappingKey);
                matcherRepository.unRegister(method, url);
                requestMappingHandlerMapping.unregisterMapping(getRequestMapping(method, url));
                SpringContextHolder.publishEvent(new TableApiReleaseEvent(api, ApiEventType.Revoke));
            }
        }
    }

    private RequestMappingInfo getRequestMapping(String method, String url) {
        return RequestMappingInfo.paths(url)
                .methods(RequestMethod.valueOf(method.toUpperCase()))
                .produces(MediaType.APPLICATION_JSON_VALUE)
                .options(requestMappingHandlerMapping.getBuilderConfiguration())
                .build();
    }

}
